package com.lmaos.basetools.dbconn;

import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.lmaos.allcontrol.beancontrol.field_.FieldUtils;
import com.lmaos.basetools.base.SArrayUtils;
import com.lmaos.basetools.base.SBaseUtils;
import com.lmaos.basetools.base.SNumberUtils;
import com.lmaos.basetools.sText.TextUtils;
import com.lmaos.basetools.sText.XmlUtils;

public class DBConf {
	public static Map<String, BasicDataSource> getDBDataSourcesByClassPath(String fileName){
		InputStream in = DBConf.class.getClassLoader().getResourceAsStream(fileName);
		return getDBDataSources(in, fileName.endsWith(".xml"));
	}
	public static Map<String, BasicDataSource> getDBDataSources(InputStream in,boolean xml){
		Map<String, Properties> resultPs = getDBConf(in, xml);
		Map<String, BasicDataSource> resultDb = new HashMap<String, BasicDataSource>();
		for(Entry<String, Properties> entry : resultPs.entrySet()){
			try {
				BasicDataSource dataSource = (BasicDataSource) BasicDataSourceFactory.createDataSource(entry.getValue());
				resultDb.put(entry.getKey(), dataSource);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return resultDb;
	}
	public static Map<String, Properties> getDBConf(InputStream in,boolean xml){
		Map<String, Properties> result = new HashMap<String, Properties>();
		try {
			if(xml){
				Document doc = XmlUtils.getDoucment(in);
				Element root = doc.getDocumentElement();
				NodeList dbs = root.getChildNodes();
				for (int i = 0; i < dbs.getLength(); i++) {
					Node dbNode =  dbs.item(i);
					if(!(dbNode instanceof Element)){
						continue;
					}
					Element db = (Element) dbNode; // 数据库节点
					String dbname = XmlUtils.getAttr(db, "name","id");
					List<Element> loop = new ArrayList<Element>();
					loop.add(db);
					for (int x = 0;x<loop.size();x++) {
						String[] includes = XmlUtils.getAttr(loop.get(x), "include").split(",");
						for (String include : includes) {
							Element dbInclude = XmlUtils.indexElementByAttr(dbs, new String[]{"name","id"}, include);
							if(dbInclude!=null){
								loop.add(dbInclude);
							}
						}
					}
					Properties dbPs = new Properties();
					result.put(dbname, dbPs); // 添加结果
					for (int x=1;x<=loop.size();x++) {
						Element element = loop.get(loop.size()-x);
						if(element == null){
							continue;
						}
						NodeList dbPropertys = element.getChildNodes();
						for (int j = 0; j < dbPropertys.getLength(); j++) {
							Node propertyNode=dbPropertys.item(j);
							if(!(propertyNode instanceof Element)){
								continue;
							}
							Element property = (Element) propertyNode;
							
							String name = property.getAttribute("name"); // 获取属性名
							String value= XmlUtils.getAttrOrContent(property, "value");// 获取属性值
							if(value==null||value.isEmpty()){			 // 属性值
								value = property.getTextContent();
							}
							dbPs.put(name, value);
						}
					}
				}
			}else{
				Properties rootPs = new Properties();
				rootPs.load(in);
				for (Entry<Object,Object> entry : rootPs.entrySet()) {
					String key = String.valueOf(entry.getKey());
					String name_Key[] = key.split(".");
					if(name_Key.length == 2){ // name.key=value
						String name = name_Key[0];
						Properties subPs = result.get(name);
						if(subPs == null){ // 不存在创建属性配置
							subPs = new Properties();
							result.put(name, subPs);
						}
						subPs.put(name_Key[1], entry.getValue());
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			try {
				in.close();
			} catch (Exception e2) {
			}
		}
		return result;
	}
	public static Map<String,Map<String, Sql>> getXmlSqlsByClassPath(String fileName){
		InputStream in = DBConf.class.getClassLoader().getResourceAsStream(fileName);
		return getXmlSqls(in);
	}
	public static Map<String,Map<String, Sql>> getXmlSqls(InputStream in){
		Map<String,Map<String, Sql>> result = new HashMap<String,Map<String, Sql>>();
		try {
			Document doc = XmlUtils.getDoucment(in);
			Element root = doc.getDocumentElement();
			NodeList groupNodes = root.getChildNodes();
			for (int i = 0; i < groupNodes.getLength(); i++) {
				Element groupNode = XmlUtils.getElement(groupNodes, i);
				if(groupNode!=null){
					String groupname = XmlUtils.getAttr(groupNode, "name","id");
					Map<String, Sql> group = result.get(groupname);
					if(group == null){
						group = new HashMap<String, Sql>();
						result.put(groupname, group);
					}
					NodeList sqlNodes = groupNode.getChildNodes();
					for (int j = 0; j < sqlNodes.getLength(); j++) {
						Element sqlNode = XmlUtils.getElement(sqlNodes, j);
						if(sqlNode!=null){
							String sqlname = XmlUtils.getAttr(sqlNode, "name","id"); 
							String sql = sqlNode.getTextContent();
							
							group.put(sqlname, toSql(sql));
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			SBaseUtils.close(in);
		}
		return result;
	}
	
	public static class Sql{
		String sql;
		String[] paramNames;
		
		public Sql(String sql, String[] paramNames) {
			super();
			this.sql = sql;
			this.paramNames = paramNames;
		}
		public String getSql(Object obj) {
			if(obj == null){
				return sql;
			}
			// TODO 将关键字转义 暂未实现
			// ... 
			
			return sql;
		}
		public void setSql(String sql) {
			this.sql = sql;
		}
		
		public String[] getParamNames() {
			return paramNames;
		}
		public void setParamNames(String[] paramNames) {
			this.paramNames = paramNames;
		}
		public Object[] getParams(Object... objs){
			if(objs==null||objs.length==0){
				return null;
			}
			return getParams((Object)objs);
		}
		
		public Object[] getParams(Object obj){
			if(paramNames == null||obj == null){
				return null;
			}
			Object[] params = new Object[paramNames.length];
			boolean array = SArrayUtils.isArrays(obj);	// 当前对象是否是数组
			int length = array?SArrayUtils.getLength(obj):1; 
			for (int i = 0; i < paramNames.length; i++) {
				String paramName = paramNames[i];// 参数名
				Object upObj = obj;				//  使用对象
				if(array){ 						// 如果这个对象是一个数组
					String[] indexAndParamName = paramName.split("\\."); // index.paramName
					if(indexAndParamName.length < 2){
						upObj = SArrayUtils.get(obj, 0);
					}else{
						int index_obj = SNumberUtils.toInt(indexAndParamName[0]);
						if(index_obj >= length){	// 查询的对象下标不存在跳过当前录入
							continue;
						}
						upObj = SArrayUtils.get(obj, index_obj);// 获得指定的对象
						paramName = indexAndParamName[1];		// 获得字段名
					}
				}//--array end
				// 设置值
				params[i] = FieldUtils.getFieldValue(upObj, paramName);
			}
			return params;
		}
		@Override
		public String toString() {
			return sql+Arrays.toString(paramNames);
		}
	}
	public static Sql toSql(String sql){
		List<Integer> indexs = TextUtils.findIndexs(sql, "#{", "\\");
		String[] paramNames = new String[indexs.size()];
		StringBuilder nSql = new StringBuilder(100);
		int endIndex = 0;
		for (int i=0;i<paramNames.length;i++) {
			Integer index = indexs.get(i);
			nSql.append(sql.substring(endIndex,index)).append("?");
			endIndex = sql.indexOf("}",index);
			paramNames[i] = sql.substring(index+2,endIndex);
			endIndex++;
		}
		nSql.append(sql.substring(endIndex));
		sql = nSql.toString();
		return new Sql(sql, paramNames);
	}
	
	
	public static void main(String[] args) throws SQLException {
//		Connection conn = getDBDataSourcesByClassPath("DataSource.xml").get("xx").getConnection();
//		System.out.println(DBConn.select("select * from content", conn).get(0).getInt("recUserId"));
//		conn.close();
		System.out.println(toSql("insert info xx (id,x) values (?)"));
		Sql sql = toSql("select * from t where u=#{u} and b=#{1.b}");
		Object[] params = sql.getParams(new A(),new B());
		
		System.out.println(sql);
	}
	public static class A{
		private int u = 10;
	}
	public static class B{
		private int b = 20;
	}
}
